Java中树形菜单分析和实现 - 很酷呦 - CSDN博客

创建时间:2019/3/26 19:51
来源:https://blog.csdn.net/weixin_39723544/article/details/80735877


版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39723544/article/details/80735877

背景概述

  • 最近在搭建基于Spring Boot(2.0.2)种子项目框架时,遇到了树形菜单加载问题。特此把解决的问题方案记录下去,供其他小伙伴参考和日后回顾。

方案分析

  • 方案选择
    • 一次性加载完,返回前台需要的数据结构
    • 点击加载,默认记载根层级的菜单。后续点击那一级菜单加载其下的子节点
  • 方案实现
    • 在这里只分析一次性加载实现。第二种实现起来更简单。在这里略过。有兴趣可以自己研究一下。

具体实现

  • 表、数据等基础资料
    • 表结构
      这里写图片描述
    • 测试数据(忽略内容啊)
      这里写图片描述
    • Vo对象字段

      private String informationTypeId; // 菜单id
      private Integer informationTypeNum;
      private String informationTypeCode;
      private String informationTypeUrl;
      private String informationTypeName;
      private String informationTypePicBosKey;
      private Integer informationTypeLevel;
      private String type;
      private String model;
      private Boolean isEdit;
      private String parentId; // 父菜单id
      private String remark;
      private String createUserId;
      private Date createTime;
      private String modifyUserId;
      private Date modifyTime;
      private Date timeStamp;
      private List<InformationTypeEntity> childNodes = new ArrayList<>(); // 子菜单list
  • 查出所有菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        // 加载所有菜单列表
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 找出根节点菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            // 找出根级菜单,(约定所有根级菜单的parentId属性为-1)
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 循环找出每个子节点的菜单列表
    /**
     * 查询资讯菜单(一次性加载)
     *
     * @throws CustomException
     */
    @RequestMapping(value = "/findAllTypeList")
    public List<InformationTypeEntity> findAllTypeList() throws CustomException {
        List<InformationTypeEntity> rootList = informationTypeService.findInfoTypeByType("1", null);
        List<InformationTypeEntity> list = new ArrayList<>();
        for (int i = 0; i < rootList.size(); i++) {
            InformationTypeEntity typeEntity = rootList.get(i);
            // 找出根级菜单,(约定所有根级菜单的parentId属性为-1)
            if ("-1".equals(typeEntity.getParentId())) {
                list.add(typeEntity);
            }
        }

        // 循环根级菜单,为每个根级菜单下的子菜单list赋值。
        for (InformationTypeEntity entity : list) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return list;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 递归解决无限层级嵌套(里面的注释我写的很清楚,英文的,大家凑合看吧。大概的思路就是除了当前子菜单后,还要考虑子菜单的子菜单,考虑用递归处理。递归结束的条件就是没有子菜单,也就是list的size为0.此种方式支持无线的嵌套)
    /**
     * Return the list of children node from percent menu id
     *
     * @param id  current menu id
     * @param rootList  all list of menus
     * @return
     * @throws CustomException
     */
    private List<InformationTypeEntity> getChildNodes(String id, List<InformationTypeEntity> rootList) throws CustomException {
        // The list of child nodes
        List<InformationTypeEntity> childList = new ArrayList<>();
        // Fill the list of child'nodes which parent id equal params of id
        for (InformationTypeEntity typeEntity : rootList) {
            if (StringUtils.isNotBlank(typeEntity.getParentId())) {
                if (id.equals(typeEntity.getParentId())) {
                    childList.add(typeEntity);
                }
            }
        }
        if (childList.size() == 0) {
            return null;
        }
        // Look up it's child node and fill
        for (InformationTypeEntity entity : childList) {
            entity.setChildNodes(getChildNodes(entity.getInformationTypeId(), rootList));
        }
        return childList;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

总结

  • 关于树形菜单的实现方案有很多,我只是列举出比较典型的2种方案。
  • 举例的这种方案可以支持无限级嵌套
  • 举例的这种只发送一次sql,可以在一定程度上调高效率,减少数据库压力
  • 理解思路,上面的代码好看优化哈,以后有时间再补充上

测试结果如下

[
  {
    "informationTypeId": "4",
    "informationTypeNum": 1,
    "informationTypeCode": "001",
    "informationTypeUrl": "/index",
    "informationTypeName": "资讯管理",
    "informationTypePicBosKey": "pic",
    "informationTypeLevel": null,
    "type": "1",
    "model": "1",
    "isEdit": null,
    "parentId": "-1",
    "remark": null,
    "createUserId": null,
    "createTime": null,
    "modifyUserId": null,
    "modifyTime": null,
    "timeStamp": "2018-06-15 10:17:51",
    "childNodes": [
      {
        "informationTypeId": "1",
        "informationTypeNum": 1,
        "informationTypeCode": "001001",
        "informationTypeUrl": "/index1",
        "informationTypeName": "十九大管理",
        "informationTypePicBosKey": "pic1",
        "informationTypeLevel": null,
        "type": "1",
        "model": "1",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": "2018-06-01 10:15:35",
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:56",
        "childNodes": [
          {
            "informationTypeId": "5",
            "informationTypeNum": 1,
            "informationTypeCode": "001001001",
            "informationTypeUrl": "/index/index1",
            "informationTypeName": "中国梦",
            "informationTypePicBosKey": "pic",
            "informationTypeLevel": null,
            "type": "1",
            "model": "2",
            "isEdit": null,
            "parentId": "1",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:18:53",
            "childNodes": [
              {
                "informationTypeId": "8",
                "informationTypeNum": 1,
                "informationTypeCode": "001001001001",
                "informationTypeUrl": "/index/index/index",
                "informationTypeName": "白日梦",
                "informationTypePicBosKey": "pic",
                "informationTypeLevel": null,
                "type": "1",
                "model": "1",
                "isEdit": null,
                "parentId": "5",
                "remark": null,
                "createUserId": null,
                "createTime": null,
                "modifyUserId": null,
                "modifyTime": null,
                "timeStamp": "2018-06-15 11:21:50",
                "childNodes": [
                  {
                    "informationTypeId": "10",
                    "informationTypeNum": 1,
                    "informationTypeCode": "001001001001001",
                    "informationTypeUrl": "/index/index/index2",
                    "informationTypeName": "李白的白日梦",
                    "informationTypePicBosKey": "pic",
                    "informationTypeLevel": null,
                    "type": "1",
                    "model": "1",
                    "isEdit": null,
                    "parentId": "8",
                    "remark": null,
                    "createUserId": null,
                    "createTime": null,
                    "modifyUserId": null,
                    "modifyTime": null,
                    "timeStamp": "2018-06-15 11:24:05",
                    "childNodes": [
                      {
                        "informationTypeId": "12",
                        "informationTypeNum": 1,
                        "informationTypeCode": "000013123123123123123",
                        "informationTypeUrl": "12341234123123",
                        "informationTypeName": "李白的白日梦儿子1",
                        "informationTypePicBosKey": "23213",
                        "informationTypeLevel": null,
                        "type": "1",
                        "model": "1",
                        "isEdit": null,
                        "parentId": "10",
                        "remark": null,
                        "createUserId": null,
                        "createTime": null,
                        "modifyUserId": null,
                        "modifyTime": null,
                        "timeStamp": "2018-06-15 11:32:36",
                        "childNodes": [
                          {
                            "informationTypeId": "14",
                            "informationTypeNum": 2,
                            "informationTypeCode": "242424",
                            "informationTypeUrl": "24243234234",
                            "informationTypeName": "l李白的白日梦儿子1的儿子11111",
                            "informationTypePicBosKey": "2424",
                            "informationTypeLevel": null,
                            "type": "1",
                            "model": "2",
                            "isEdit": null,
                            "parentId": "12",
                            "remark": null,
                            "createUserId": null,
                            "createTime": null,
                            "modifyUserId": null,
                            "modifyTime": null,
                            "timeStamp": "2018-06-15 11:36:28",
                            "childNodes": null
                          }
                        ]
                      },
                      {
                        "informationTypeId": "13",
                        "informationTypeNum": 2,
                        "informationTypeCode": "2424234234234234",
                        "informationTypeUrl": "2424242424",
                        "informationTypeName": "李白的白日梦儿子2",
                        "informationTypePicBosKey": "23424",
                        "informationTypeLevel": null,
                        "type": "1",
                        "model": "1",
                        "isEdit": null,
                        "parentId": "10",
                        "remark": null,
                        "createUserId": null,
                        "createTime": null,
                        "modifyUserId": null,
                        "modifyTime": null,
                        "timeStamp": "2018-06-15 11:33:07",
                        "childNodes": null
                      }
                    ]
                  },
                  {
                    "informationTypeId": "11",
                    "informationTypeNum": 2,
                    "informationTypeCode": "0010001001001002",
                    "informationTypeUrl": "/index/index/index2",
                    "informationTypeName": "杜甫的白日梦",
                    "informationTypePicBosKey": "pic",
                    "informationTypeLevel": null,
                    "type": "1",
                    "model": "2",
                    "isEdit": null,
                    "parentId": "8",
                    "remark": null,
                    "createUserId": null,
                    "createTime": null,
                    "modifyUserId": null,
                    "modifyTime": null,
                    "timeStamp": "2018-06-15 11:24:58",
                    "childNodes": null
                  }
                ]
              },
              {
                "informationTypeId": "9",
                "informationTypeNum": 2,
                "informationTypeCode": "001001001002",
                "informationTypeUrl": "/index/index/index2",
                "informationTypeName": "黑夜梦",
                "informationTypePicBosKey": "pic",
                "informationTypeLevel": null,
                "type": "1",
                "model": "2",
                "isEdit": null,
                "parentId": "5",
                "remark": null,
                "createUserId": null,
                "createTime": null,
                "modifyUserId": null,
                "modifyTime": null,
                "timeStamp": "2018-06-15 11:22:30",
                "childNodes": null
              }
            ]
          },
          {
            "informationTypeId": "6",
            "informationTypeNum": 2,
            "informationTypeCode": "001001002",
            "informationTypeUrl": "/index/index2",
            "informationTypeName": "扶贫",
            "informationTypePicBosKey": "pic",
            "informationTypeLevel": null,
            "type": "1",
            "model": "2",
            "isEdit": null,
            "parentId": "1",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:20:23",
            "childNodes": null
          }
        ]
      },
      {
        "informationTypeId": "2",
        "informationTypeNum": 2,
        "informationTypeCode": "001002",
        "informationTypeUrl": "/index2",
        "informationTypeName": "党代会议管理",
        "informationTypePicBosKey": "pic2",
        "informationTypeLevel": null,
        "type": "1",
        "model": "2",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": "2018-06-13 05:48:42",
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:54",
        "childNodes": [
          {
            "informationTypeId": "7",
            "informationTypeNum": 1,
            "informationTypeCode": "001002001",
            "informationTypeUrl": "/index/index",
            "informationTypeName": "党会管理",
            "informationTypePicBosKey": "pic111",
            "informationTypeLevel": null,
            "type": "1",
            "model": "3",
            "isEdit": null,
            "parentId": "2",
            "remark": null,
            "createUserId": null,
            "createTime": null,
            "modifyUserId": null,
            "modifyTime": null,
            "timeStamp": "2018-06-15 10:20:59",
            "childNodes": null
          }
        ]
      },
      {
        "informationTypeId": "3",
        "informationTypeNum": 3,
        "informationTypeCode": "001003",
        "informationTypeUrl": "/index3",
        "informationTypeName": "两学一做",
        "informationTypePicBosKey": "pic3",
        "informationTypeLevel": null,
        "type": "1",
        "model": "1",
        "isEdit": null,
        "parentId": "4",
        "remark": null,
        "createUserId": null,
        "createTime": null,
        "modifyUserId": null,
        "modifyTime": null,
        "timeStamp": "2018-06-15 10:17:53",
        "childNodes": null
      }
    ]
  }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289